package com.shuangxi.spring;

import com.shuangxi.config.AppConfig;
import com.shuangxi.service.UserService;
import com.sun.xml.internal.ws.util.StringUtils;

import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

public class MyApplicationContext {

    private Class aClass;

    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//存储beanInfo的信息

    private ConcurrentHashMap<String, Object> singletonBeanMap = new ConcurrentHashMap<>();//单例池

    private ArrayList<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public MyApplicationContext(Class appConfigClass) {
        this.aClass = appConfigClass;
        //获取扫描路径
        if (appConfigClass.isAnnotationPresent(ComponentScan.class)) {//判断是否存在ComponentScan注解
            ComponentScan componentScan = (ComponentScan) appConfigClass.getAnnotation(ComponentScan.class);//取出注解对象
            String path = componentScan.value();//获取扫描路径
            path = path.replace(".", "/");//对路径进行处理

            ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file = new File(resource.getFile());//取到文件夹路径
            if (file.isDirectory()) {//是否是文件夹
                File[] files = file.listFiles();//取出文件夹下的所有文件
                for (File f : files) {//遍历所有文件
                    String fileName = f.getAbsolutePath();//获取文件的全路径名
                    if (fileName.endsWith(".class")) { //判断文件是否是".class"结尾
                        //对符合要求的文件名进行处理
                        path = path.replace("/", "\\");
                        String className = fileName.substring(fileName.indexOf(path), fileName.indexOf(".class"));//截取路径
                        className = className.replace("\\", ".");//拼接类路径
                        try {
                            Class<?> clazz = classLoader.loadClass(className);//反射取出class对象
                            if (clazz.isAnnotationPresent(Component.class)) {//判断当前类是否有Component注解
                                //添加BeanPostProcessor的实现类对象，以方便之后处理
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                    beanPostProcessors.add(instance);
                                    continue;
                                }
                                //获取bean名
                                String beanName = getBeanName(clazz);
                                BeanDefinition beanDefinition = new BeanDefinition();//将bean的信息封装到beanDefinition中
                                beanDefinition.setType(clazz);
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    String value = clazz.getAnnotation(Scope.class).value();
                                    if (value == null || "".equals(value)) beanDefinition.setScope("singleton");
                                    else beanDefinition.setScope(value);
                                } else {
                                    beanDefinition.setScope("singleton");
                                }
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        //创建单例的bean
        for (String key : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(key);
            if ("singleton".equals(beanDefinition.getScope())) {
                singletonBeanMap.put(key, createBean(key, beanDefinition));
            }
        }
    }

    private Object createBean(String key, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();
        try {
            Object o = clazz.newInstance();
            //依赖注入
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(o, getBean(field.getName()));
                }
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                o = beanPostProcessor.postProcessBeforeInitialization(key, o);
            }
            //Aware
            if (o instanceof BeanNameAware) ((BeanNameAware) o).setBeanName(key);
            //初始化
            if (o instanceof InitializingBean) ((InitializingBean) o).afterPropertiesSet();

            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                o = beanPostProcessor.postProcessAfterInitialization(key, o);
            }
            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Object getBean(String className) {
        return handleBean(className);
    }

    public Object getBean(Class clazz) {
        if (clazz.isAnnotationPresent(Component.class)) {
            String beanName = getBeanName(clazz);
            return handleBean(beanName);
        } else {
            throw new NullPointerException();
        }
    }

    public Object handleBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        } else {
            //如果是单例
            if ("singleton".equals(beanDefinition.getScope())) {
                Object bean = singletonBeanMap.get(beanName);
                if (bean == null) {
                    bean = createBean(beanName, beanDefinition);
                    singletonBeanMap.put(beanName, bean);
                }
                return bean;
            } else { //多例
                return createBean(beanName, beanDefinition);
            }
        }
    }

    public String getBeanName(Class clazz) {
        Component annotation = (Component) clazz.getAnnotation(Component.class);
        String beanName = annotation.value();
        if (beanName == null || "".equals(beanName)) {
            String simpleName = clazz.getSimpleName();
            beanName = Introspector.decapitalize(simpleName);//首字母小写
        }
        return beanName;
    }
}
